/**
*    Copyright (C) 2008  Chase Kernan 
*    chase.kernan@gmail.com
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
* 
**/
package com.chasekernan.hxnova.componenteditor;

import com.chasekernan.hxnova.core.components.Armor;
import com.chasekernan.hxnova.core.components.BeamWeapon;
import com.chasekernan.hxnova.core.components.Bomb;
import com.chasekernan.hxnova.core.components.CargoPod;
import com.chasekernan.hxnova.core.components.ComponentSet;
import com.chasekernan.hxnova.core.components.Colonizer;
import com.chasekernan.hxnova.core.components.Component;
import com.chasekernan.hxnova.core.components.Computer;
import com.chasekernan.hxnova.core.components.Engine;
import com.chasekernan.hxnova.core.components.Hull;
import com.chasekernan.hxnova.core.components.Missile;
import com.chasekernan.hxnova.core.components.Scanner;
import com.chasekernan.hxnova.core.components.Shield;
import com.chasekernan.hxnova.core.minerals.MineralSet;
import com.chasekernan.hxnova.core.races.LRTSet;
import com.chasekernan.hxnova.core.races.PRTSet;
import com.chasekernan.hxnova.core.races.TechLevel;
import com.chasekernan.hxnova.utils.Rectangle;
import com.chasekernan.hxnova.utils.Vector;

/**
    This class communicates with the AS3-Flex front-end and the neko backend.
    In order to not deal with using haxe classes in AS3, this just uses dynamic
    objects.
**/
class Lib {
    
    static function main() {}
    
    public var components : Class<Dynamic>;
    public var ui : Dynamic;
    
    private var cnx : swhx.Connection;
    
    public function new() {
        components = ComponentSet;
        
        var c = new Computer("Test Computer", 2, 0.45);
        ComponentSet.addComponent(c);
        
        var a = new Armor("Test Armor",  100);
        ComponentSet.addComponent(a);
        
        cnx = swhx.Connection.desktopConnect();
    }
    
    /**
        Returns the names of all of the components of a given type.
    **/
    public function getComponents(type : String) : Array < String > {
        var names = new Array<String>();
        
        var c = Type.resolveClass("com.chasekernan.hxnova.core.components." 
                + type);
        
        for (component in ComponentSet.getAllComponentsOfType(c)) 
                names.push(component.name);
        
        return names;
    }
    
    /**
        Given an object with the components fields, it saves a component
        with the given type.
    **/
    public function saveComponent(type : String, details : Dynamic) {
        var name = details.name;
        var description = details.description;
        var mass = details.mass;
        var pic = details.picture;
        var resourceCost = details.resourceCost;
        
        var minerals : MineralSet = new MineralSet();
        minerals.setValue("ironium", details.ironiumCost);
        minerals.setValue("boranium", details.boraniumCost);
        minerals.setValue("germanium", details.germaniumCost);
        
        var tech : TechLevel = new TechLevel();
        tech.setLevel("energy", details.energy);
        tech.setLevel("weapons", details.weapons);
        tech.setLevel("construction", details.construction);
        tech.setLevel("propulsion", details.propulsion);
        tech.setLevel("electronics", details.electronics);
        tech.setLevel("biotechnology", details.biotechnology);
        
        var lrts = resolveLRTs(details.lrts);
        
        var prtStringA = cast(details.prts, String).split(', ');
        var prts = new Array<PRT>();
        for (prt in prtStringA) prts.push(PRTSet.getPRTByName(prt));
        
        var c : Component;
        
        switch(type) {
            case "Armor":
                c = new Armor(name, details.strength, details.strengthShield);
            case "BeamWeapon":
                c = new BeamWeapon(name, details.power, details.range, 
                        details.initiative, details.isGattling, 
                        details.isSapper);
            case "Bomb":
                c = new Bomb(name, details.minimumKill, details.percentKill, 
                        details.installationsDestroyed);
            case "CargoPod":
                c = new CargoPod(name, details.mineralCapacity, 
                        details.fuelCapacity);
            case "Colonizer":
                c = new Colonizer(name);
            case "Computer":
                c = new Computer(name, details.initiative, details.accuracy);
            case "Engine":
                var en : Engine = new Engine(name, details.isRamscoop, 
                        details.combatSpeed);
                for (i in Engine.WARP_MIN...(Engine.WARP_MAX + 1)) 
                        en.setEfficiency(i, details.efficiency[i]);
                c = en;
            case "Hull":
                var h : Hull = new Hull(name, details.fuelCapacity, 
                        details.baseArmor, details.baseInitiative);
                for (p in cast(details.parts, Array<Dynamic>)) 
                    h.addPart(ComponentSet.getComponentTypeSetByName(p.type), 
                            p.amount, new Rectangle(new Vector(p.x, p.y), 
                            new Vector(p.width, p.height)));
                for (s in cast(details.storage, Array<Dynamic>)) 
                    h.addStorageSpace(s.amount, new Rectangle(
                            new Vector(s.x, s.y), 
                            new Vector(s.width, s.height)));
                c = h;
            case "Missile":
                c = new Missile(name, details.power, details.range, 
                        details.initiative, details.accuracy, 
                        details.isCapitalMissile);
            case "Scanner":
                c = new Scanner(name, details.range, details.penetratingRange);
            case "Shield":
                c = new Shield(name, details.strength, details.strengthArmor);
        }
        
        c.description = description;
        c.picture = pic;
        c.availableToPRTs = prts;
        c.lrts = lrts;
        c.mass = mass;
        c.requiredTech = tech;
        c.resourceCost = resourceCost;
        c.mineralCost = minerals;
        
        if (!checkName(name)) ComponentSet.setComponent(c);
        else ComponentSet.addComponent(c);
    }
    
    /**
        Returns the fields of a component with the given name.
    **/
    public function getDetails(name : String) : Dynamic {
        var c : Component = ComponentSet.getComponentByName(name);
        var details : Dynamic = Reflect.empty();
        
        details.name = c.name;
        details.description = c.description;
        details.mass = c.mass;
        details.picture = c.picture;
        details.resourceCost = c.resourceCost;
        
        details.ironiumCost = c.mineralCost.getValue("ironium");
        details.boraniumCost = c.mineralCost.getValue("boranium");
        details.germaniumCost = c.mineralCost.getValue("germanium");
        
        details.energy = c.requiredTech.getLevel("energy");
        details.weapons = c.requiredTech.getLevel("weapons");
        details.construction = c.requiredTech.getLevel("construction");
        details.propulsion = c.requiredTech.getLevel("propulsion");
        details.electronics = c.requiredTech.getLevel("electronics");
        details.biotechnology = c.requiredTech.getLevel("biotechnology");
        
        details.prts = c.availableToPRTs.join(", ");
        details.lrts = switch(c.lrts) {
            case Any:
                "any";
            case Not(lrts):
                "not " + lrts.join(", ");
            case Only(lrts):
                "only " + lrts.join(", ");
        }
        
        //Massive... TODO: find a way to reduce the size of this list.
        if (Type.getClass(c) == Armor) {
            var nc = cast(c, Armor);
            details.strength = nc.strength;
            details.strengthShield = nc.strengthShield;
            
        } else if (Type.getClass(c) == BeamWeapon) {
            var nc = cast(c, BeamWeapon);
            details.power = nc.power;
            details.range = nc.range;
            details.initiative = nc.initiative;
            details.isGattling = nc.isGattling;
            details.isSapper = nc.isSapper;
            
        } else if (Type.getClass(c) == Bomb) {
            var nc = cast(c, Bomb);
            details.minimumKill = nc.minimumKill;
            details.percentKill = nc.percentKill;
            details.installationsDestroyed = nc.installationsDestroyed;
            
        } else if (Type.getClass(c) == CargoPod) {
            var nc = cast(c, CargoPod);
            details.mineralCapacity = nc.mineralCapacity;
            details.fuelCapacity = nc.fuelCapacity;
            
        } else if (Type.getClass(c) == Colonizer) {
            //no properties
            
        } else if (Type.getClass(c) ==  Computer) {
            var nc = cast(c, Computer);
            details.initiative = nc.initiative;
            details.accuracy = nc.accuracy;
            
        } else if (Type.getClass(c) == Engine) {
            var nc = cast(c, Engine);
            details.isRamscoop = nc.isRamscoop;
            details.combatSpeed = nc.combatSpeed;
            
            details.efficiency = new Array<Int>();
            for (i in Engine.WARP_MIN...(Engine.WARP_MAX + 1)) 
                details.efficiency[i] = nc.getEfficiency(i);
            
        } else if (Type.getClass(c) == Hull) {
            var nc = cast(c, Hull);
            details.fuelCapacity = nc.fuelCapacity;
            details.baseArmor = nc.baseArmor;
            details.baseInitiative = nc.baseInitiative;
            
            details.parts = new Array<Dynamic>();
            for (p in nc.parts) 
                details.parts.push( { type : p.type.name, amount : p.amount, 
                        x : p.position.topLeft.x, y : p.position.topLeft.y, 
                        width : p.position.dimensions.x, 
                        height : p.position.dimensions.y } );
            
            details.storage = new Array<Dynamic>();
            for (p in nc.storage) 
                details.parts.push( {amount : p.amount, 
                        x : p.position.topLeft.x, y : p.position.topLeft.y, 
                        width : p.position.dimensions.x, 
                        height : p.position.dimensions.y } );
            
        } else if (Type.getClass(c) == Missile) {
            var nc = cast(c, Missile);
            details.power = nc.power;
            details.range = nc.range;
            details.initiative = nc.initiative;
            details.accuracy = nc.accuracy;
            details.isCapitalMissile = nc.isCapitalMissile;
            
        } else if (Type.getClass(c) == Scanner) {
            var nc = cast(c, Scanner);
            details.range = nc.range;
            details.penetratingRange = nc.penetratingRange;
            
        } else if (Type.getClass(c) == Shield) {
            var nc = cast(c, Shield);
            details.strength = nc.strength;
            details.strengthArmor = nc.strengthArmor;
        }
        
        return details;
    }
    
    /**
        Returns true if the name hasn't been used yet.
    **/
    public function checkName(name : String) : Bool {
        try {
            var c = ComponentSet.getComponentByName(name);
            return c == null;
        } catch (e : Dynamic) {
            return true;
        }
    }
    
    /**
        Adds a new component with the given name and type.
    **/
    public function createNew(type : String, name : String) {
        ComponentSet.addComponent(Type.createInstance(Type.resolveClass(
                "com.chasekernan.hxnova.core.components." + type), [name]));
    }
    
    private static function resolveLRTs(s : String) : LRTSetting {
        var anyEReg : EReg = ~/any/i;
        var notEReg : EReg = ~/not[ ]+/i;
        var onlyEReg : EReg = ~/only[ ]+/i;
            
        if(s == "any") return Any;
        else if(notEReg.match(s)) {
            var tempArray : Array<String> = notEReg.split(s).pop().split(', ');
            var lrtArray : Array<LRT> = new Array();
            for(i in tempArray) lrtArray.push(LRTSet.getLRTByName(i));
            return Not(lrtArray);
        }
        else if(onlyEReg.match(s)) {
            var tempArray : Array<String> = onlyEReg.split(s).pop().split(', ');
            var lrtArray : Array<LRT> = new Array();
            for(i in tempArray) lrtArray.push(LRTSet.getLRTByName(i));
            return Only(lrtArray);
        } 
        else throw "LRT restrictions do not conform: " + s;
    }
    
    /**
        Loads the components from a selected file (selected by file selector in
        the base class.
    **/
    public function load() {
        try {
            var xml = Xml.parse(cnx.Base.load.call([])).firstElement();
            ComponentSet.readFromXml(xml);
            ui.refresh();
        } catch (e : Dynamic ) {
            throw "Error while loading file: " + e;
        }
    }
    
    /**
        Saves the components to a file.
    **/
    public function save() {
        cnx.Base.save.call([ComponentSet.writeToXml().toString()]);
    }
}